#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "blog_utils.h"
#include "builtins.h"

#define SHORTEN_LEN 100
#define SHORTEN_DOTLEN 3

static int blog_sql_limit;

__BLTIN_ST db_datatype_t types_slct[] = BLOG_SELECT_TYPES;
__BLTIN_ST char *sql_slct = BLOG_SELECT;

__BLTIN_ST db_datatype_t types_ctnt[] = BLOG_SELECT_CONTENT_TYPES;
__BLTIN_ST char *sql_ctnt = BLOG_SELECT_CONTENT;

__BLTIN_ST db_datatype_t types_ctup[] = BLOG_CONTENT_VIEWS_TYPES;
__BLTIN_ST char *sql_ctup = BLOG_CONTENT_VIEWS;

__BLTIN_ST db_datatype_t types_cmmt[] = BLOG_COMMENT_SHOW_TYPES;
__BLTIN_ST char *sql_cmmt = BLOG_COMMENT_SHOW;

__BLTIN_RT db_stmt_t *stmt_slct, *stmt_ctnt, *stmt_ctup, *stmt_cmmt;
__BLTIN_RT void *db_slct_ptr, *db_ctnt_ptr, *db_ctup_ptr, *db_cmmt_ptr;


void
blog_utils_init(void *db_handle)
{
  blog_sql_limit = atoi(BLOG_LIMIT_DEF);
  stmt_slct = DB_STMT(types_slct);
  stmt_ctnt = DB_STMT(types_ctnt);
  stmt_ctup = DB_STMT(types_ctup);
  stmt_cmmt = DB_STMT(types_cmmt);
  db_exec_simple(db_handle, "PRAGMA foreign_keys = 1;", NULL);
}

void
blog_utils_free(void)
{
  DB_STMTFREE(db_slct_ptr, stmt_slct, types_slct);
  DB_STMTFREE(db_ctnt_ptr, stmt_ctnt, types_ctnt);
  DB_STMTFREE(db_ctup_ptr, stmt_ctup, types_ctup);
  DB_STMTFREE(db_cmmt_ptr, stmt_cmmt, types_cmmt);
}

int
blog_create_tables(void)
{
  int ret;
  void *dbhndl;

  CHECK_RET(db_init(BLOG_DB_PRFIX, &dbhndl), ret);
  CHECK_RET(db_vexec_simple(dbhndl, BLOG_CREATE_TABLES, NULL), ret);
  db_close(dbhndl);
  return (ret);
}

int
blog_upublish_link(void *dbhndl, bool publish, const char *link)
{
  int ret = 0, idx = 0;
  const db_datatype_t types_publ[] = BLOG_PUB_TYPES;
  const char *sql_publ = BLOG_PUBLISH;
  const char *sql_upubl = BLOG_UNPUBLISH;
  db_stmt_t *stmt = DB_STMT(types_publ);
  void *db_stmt = NULL;

  db_bind_str(stmt, &idx, link, -1);
  if (publish) {
    CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_publ, stmt, types_publ), ret);
  } else {
    CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_upubl, stmt, types_publ), ret);
  }
  DB_STMTFREE(db_stmt, stmt, types_publ);
  return (ret);
}

int
blog_new_blog_entry(void *dbhndl, const char *link, const char *title, const char *content, const char *author, const char *category, time_t date)
{
  int ret = 0, idx = 0;
  const db_datatype_t types_insr[] = BLOG_INSERT_TYPES;
  const char *sql_insr = BLOG_INSERT;
  db_stmt_t *stmt = DB_STMT(types_insr);
  void *db_stmt = NULL;

  if (date <= 0) date = time(NULL);
  db_bind_str(stmt, &idx, link, -1);
  db_bind_str(stmt, &idx, title, -1);
  db_bind_str(stmt, &idx, content, -1);
  db_bind_int(stmt, &idx, (long int *) &date);
  db_bind_str(stmt, &idx, author, -1);
  db_bind_str(stmt, &idx, category, -1);
  CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_insr, stmt, types_insr), ret);
  DB_STMTFREE(db_stmt, stmt, types_insr);
  return (ret);
}

int
blog_new_blog_entry_sd(void *dbhndl, const char *link, const char *title, const char *content, const char *author, const char *category, const char *date)
{
  struct tm tm;
  time_t time;

  memset(&tm, 0, sizeof(struct tm));
  strptime(date, "%Y-%m-%d %H:%M:%S", &tm);
  time = mktime(&tm);
  return (blog_new_blog_entry(dbhndl, link, title, content, author, category, time));
}

static void
blog_show_post_shorten(char *content, size_t newlen)
{
  size_t len = strlen(content);

  if (len-SHORTEN_DOTLEN < newlen) {
    newlen = len-SHORTEN_DOTLEN;
  }
  memset((char *)(content+newlen), '\0', len-newlen);
  if (newlen >= SHORTEN_DOTLEN) memset((char *)(content+newlen-SHORTEN_DOTLEN), '.', SHORTEN_DOTLEN);
}

static int
blog_show_post(const tmpl_t *t, tmpl_data_t *tdata, db_result_t *db_rs, bool short_html)
{
  int i = 0;

  while (db_rs) {
    if (db_rs->data) {
      switch (i) {
        case 0:
          tmpl_setf_val(t, tdata, "id", "%d", *(int *)db_rs->data);
          break;
        case 1:
          tmpl_set_val(t, tdata, "title", (char *)db_rs->data);
          break;
        case 2:
          tmpl_setf_val(t, tdata, "date", "%d", *(int *)db_rs->data);
          break;
        case 3:
          tmpl_set_val(t, tdata, "link", (char *)db_rs->data);
          break;
        case 4:
          tmpl_set_val(t, tdata, "author_name", (char *)db_rs->data);
          break;
        case 5:
          tmpl_set_val(t, tdata, "author", (char *)db_rs->data);
          break;
        case 6:
          tmpl_set_val(t, tdata, "cat_name", (char *)db_rs->data);
          break;
        case 7:
          tmpl_set_val(t, tdata, "cat", (char *)db_rs->data);
          break;
        case 8:
          tmpl_setf_val(t, tdata, "pubdate", "%d", *(int *)db_rs->data);
          break;
        case 9:
          tmpl_setf_val(t, tdata, "views", "%d", *(int *)db_rs->data);
          break;
        case 10:
          if (short_html) {
            blog_show_post_shorten((char *)db_rs->data, SHORTEN_LEN);
          }
          tmpl_set_val(t, tdata, "html", (char *)db_rs->data);
          break;
        default:
          W("blog: unknown db_result id: %d", i);
      }
    } else return (0);
    db_rs = db_rs->nextcol;
    i++;
  }
  return (i);
}

void
blog_show_posts(void *dbhndl, string_t **result, const tmpl_t *t, tmpl_data_t *tdata, const char *limit)
{
  int i, idx = 0, lim;
  db_result_t *db_rs = NULL, *row = NULL;

  lim = (int) strtol(limit, NULL, 10);
  if (lim == 0) lim = blog_sql_limit;
  db_bind_int(stmt_slct, &idx, (long int *) &lim);
  DB_EXEC(dbhndl, &db_slct_ptr, &db_rs, sql_slct, stmt_slct, types_slct);
  row = db_rs;
  i = 0;
  while (row) {
    tmpl_setf_val(t, tdata, "entry", "%d", i);
    if (blog_show_post(t, tdata, row, false) > 0) {
      *result = TMPL_RESULT(t, tdata, result);
    }
    row = row->nextrow;
    i++;
  }
  db_free_results(db_rs);
}

void
blog_show_post_content(void *dbhndl, string_t **result, const tmpl_t *t, tmpl_data_t *tdata, const char *link) {
  int idx = 0;
  db_result_t *db_rs = NULL;

  db_bind_str(stmt_ctnt, &idx, link, strlen(link));
  DB_EXEC(dbhndl, &db_ctnt_ptr, &db_rs, sql_ctnt, stmt_ctnt, types_ctnt);
  if (db_rs) {
    if (blog_show_post(t, tdata, db_rs, true) > 0) {
      *result = TMPL_RESULT(t, tdata, result);
    }
  }
  db_free_results(db_rs);
}

int
blog_add_catg(void *dbhndl, const char *nice_name, const char *name) {
  int ret = 0, idx = 0;
  const db_datatype_t types_insr[] = BLOG_CATG_ADD_TYPES;
  const char *sql_insr = BLOG_CATG_ADD;
  db_stmt_t *stmt = DB_STMT(types_insr);
  void *db_stmt = NULL;

  db_bind_str(stmt, &idx, nice_name, -1);
  db_bind_str(stmt, &idx, name, -1);
  CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_insr, stmt, types_insr), ret);
  DB_STMTFREE(db_stmt, stmt, types_insr);
  return (ret);
}

int
blog_add_tag(void *dbhndl, const char *cname, const char *nname) {
  int ret = 0, idx = 0;
  const db_datatype_t types_insr[] = BLOG_TAG_ADD_TYPES;
  const char *sql_insr = BLOG_TAG_ADD;
  db_stmt_t *stmt = DB_STMT(types_insr);
  void *db_stmt = NULL;

  db_bind_str(stmt, &idx, cname, -1);
  db_bind_str(stmt, &idx, nname, -1);
  CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_insr, stmt, types_insr), ret);
  DB_STMTFREE(db_stmt, stmt, types_insr);
  return (ret);
}

int
blog_add_tagpost(void *dbhndl, const char *link, const char *tag_cname) {
  int ret = 0, idx = 0;
  const db_datatype_t types_insr[] = BLOG_TAG_POST_ADD_TYPES;
  const char *sql_insr = BLOG_TAG_POST_ADD;
  db_stmt_t *stmt = DB_STMT(types_insr);
  void *db_stmt = NULL;

  db_bind_str(stmt, &idx, link, -1);
  db_bind_str(stmt, &idx, tag_cname, -1);
  CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_insr, stmt, types_insr), ret);
  DB_STMTFREE(db_stmt, stmt, types_insr);
  return (ret);
}

int 
blog_add_comment(void *dbhndl, const char *link, const char *author, const char *url, const char *comment, time_t date) {
  int ret = 0, idx = 0;
  const db_datatype_t types_insr[] = BLOG_COMMENT_ADD_TYPES;
  const char *sql_insr = BLOG_COMMENT_ADD;
  db_stmt_t *stmt = DB_STMT(types_insr);
  void *db_stmt = NULL;

  if (date <= 0) date = time(NULL);
  db_bind_str(stmt, &idx, link, -1);
  db_bind_str(stmt, &idx, author, -1);
  db_bind_str(stmt, &idx, url, -1);
  db_bind_str(stmt, &idx, comment, -1);
  db_bind_int(stmt, &idx, (long int *) &date);
  CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_insr, stmt, types_insr), ret);
  DB_STMTFREE(db_stmt, stmt, types_insr);
  return (ret);
}

void
blog_show_comments(void *dbhndl, const char *post_id, const char *start_date, const char *limit)
{
  int ret = 0, idx = 0, lim, pid, sdt;
  char *endptr = NULL;

  lim = (int) strtol(limit, &endptr, 10);
  if (lim == 0) {
    lim = blog_sql_limit;
  }
  pid = (int) strtol(post_id, &endptr, 10);
  sdt = (int) strtol(start_date, &endptr, 10);
  db_bind_int(stmt_cmmt, &idx, (long int *) &pid);
  db_bind_int(stmt_cmmt, &idx, (long int *) &sdt);
  db_bind_int(stmt_cmmt, &idx, (long int *) &lim);
  CHECK_RET(DB_EXEC(dbhndl, &db_cmmt_ptr, NULL, sql_cmmt, stmt_cmmt, types_cmmt), ret);
  DB_STMTFREE(db_cmmt_ptr, stmt_cmmt, types_cmmt);
}


static int __blog_tag(void *dbhndl, const char *cname, bool inc)
{
  int ret = 0, idx = 0;
  const db_datatype_t types_upd[] = BLOG_TAG_REF_TYPES;
  const char *sql_upd;
  db_stmt_t *stmt = DB_STMT(types_upd);
  void *db_stmt = NULL;

  if (inc) {
    sql_upd = BLOG_TAG_INC_REF;
  } else {
    sql_upd = BLOG_TAG_DEC_REF;
  }
  db_bind_str(stmt, &idx, cname, -1);
  CHECK_RET(DB_EXEC(dbhndl, &db_stmt, NULL, sql_upd, stmt, types_upd), ret);
  DB_STMTFREE(db_stmt, stmt, types_upd);
  return (ret);
}

int
blog_tag_inc(void *dbhndl, const char *cname) {
  return (__blog_tag(dbhndl, cname, true));
}

int
blog_tag_dec(void *dbhndl, const char *cname) {
  return (__blog_tag(dbhndl, cname, false));
}

int
blog_inc_view(void *dbhndl, const char *link) {
  int ret = 0, idx = 0;

  db_bind_str(stmt_ctup, &idx, link, -1);
  CHECK_RET(DB_EXEC(dbhndl, &db_ctup_ptr, NULL, sql_ctup, stmt_ctup, types_ctup), ret);
  DB_STMTFREE(db_ctup_ptr, stmt_ctup, types_ctup);
  return (ret);
}
